#!/usr/bin/python
# 
# Copyright (c) 2010 Edward Harvey
# 
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# 
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#

import sys, os, os.path, getopt

def usage(subcommand=""):
    if subcommand == "":
        print """
usage: zhist subcommand [options] [args]
Type 'zhist help subcommand' for help on a specific subcommand.

Available subcommands:
   help
   ls
   
"""
    elif subcommand == "ls":
        print """
usage: zhist ls [options] TARGET [TARGET...]

Generates a list of all the available snapshot versions of the specified
file or directory.

If no TARGET is specified, the present directory is assumed.

"""
    else:
        print "Unknown help option "+str(command)

def snapshotdir(pathname=''):
    if pathname.startswith('/'):
        if pathname == '/':
            if os.path.exists('/.zfs/snapshot'):
                return '/.zfs/snapshot'
            else:
                return False
        elif os.path.exists(pathname+'/.zfs/snapshot'):
            return pathname+'/.zfs/snapshot'
        else:
            return snapshotdir(os.path.dirname(pathname))
def ls():
    if len(sys.argv)<3:
        # Then the person simply did "zhist ls" and the implied target is PWD
        sys.argv.append('.')
    allargs=False   # user can specify "--" to indicate all the rest are not options, allows 
                    # users to specify files which are named "-n" and "-i"
    searchnames=True    # by default, we're not searching based on inodes
    targets=[]
    
    for arg in sys.argv[2:]:
        if not allargs:
            if arg == '-i':
                searchnames=False
            elif arg == '-n':
                searchnames=True
            elif arg == '--':
                allargs=True
        targets.append(arg) 
        
    for target in targets:
        targetabsname=os.path.abspath(target)
        snapdir=snapshotdir(targetabsname)
        suffix=targetabsname.replace(os.path.commonprefix([targetabsname,snapdir]),'',1)
        targetsnaps=[]
        exists=os.path.exists
        try:
            exists=os.path.lexists
        except:
            print "Note: No such os.lexists. You must be using python < 2.4. Therefore forced to follow symlinks. Results questionable."
            
        for snapname in os.listdir(snapdir):
            if exists(snapdir+'/'+snapname+'/'+suffix):
                mystat=os.lstat(snapdir+'/'+snapname+'/'+suffix)
                mode=mystat[0]   # protection bits
                inode=mystat[1]
                uid=mystat[4]
                gid=mystat[5]
                size=mystat[6]
                mtime=mystat[8]
                # This tuple is composed of:  ( an integer mtime, (a tuple), 'pathname' )
                # Having the mtime as the first component makes it easy to sort the whole list
                # Having the tuple, which includes mtime, allows us to easily identify repeat (non-unique) items
                # and of course, the pathname is needed in order to display the pathname.
                targetsnaps.append((mtime,(mtime,mode,inode,uid,gid,size),snapdir+'/'+snapname+'/'+suffix))
        # I'd like the output to also consider the item that's in the present filesystem,
        # and since I'm writing it, that's the behavior it will have.  ;-)
        if exists(targetabsname):
            mystat=os.lstat(targetabsname)
            mode=mystat[0]   # protection bits
            inode=mystat[1]
            uid=mystat[4]
            gid=mystat[5]
            size=mystat[6]
            mtime=mystat[8]
            targetsnaps.append((mtime,(mtime,mode,inode,uid,gid,size),targetabsname))            
        targetsnaps.sort()
        targetsnaps.reverse()
        newstat=()
        oldstat=()
        for targetsnap in targetsnaps:
            newstat=targetsnap[1]
            if newstat == oldstat:
                # not written yet:
                # if we have selected not to omit duplicates, 
                #    then print
                pass
            else:
                print targetsnap[2]   # this is the absolute name
            oldstat=newstat
                
    sys.exit(0)
def main():
    if len(sys.argv) <= 1:
        usage()
        sys.exit(2)
    elif sys.argv[1] == 'help':
        try:
            usage(sys.argv[2])
        except:
            usage()
        sys.exit(2)
    elif sys.argv[1] == 'ls':
        ls()
        sys.exit(0)
    else:
        usage()
        sys.exit(2)
    
if __name__ == "__main__":
  sys.exit(main())
